/*
	memory_page_alloc.c

	Contains the code for allocating and deallocating physical pages.
	This is the lowest level of the memory allocation hierarchy.

	Author: Aidan Goddard 04/07/14
*/

#include "memory_tables.h"
#include "memory.h"
#include "printf.h"
#include "lock.h"

///////////////////////////////////////////////////////////////////////////////////
// data structure pointers (initialised by init code)
struct physical_allocation_control *phys_allocation_control;
uint8_t *bitmap_4kb;
uint64_t bitmap_4kb_length;
uint8_t *bitmap_8kb;
uint64_t bitmap_8kb_length;
uint8_t *bitmap_16kb;
uint64_t bitmap_16kb_length;
uint8_t *bitmap_32kb;
uint64_t bitmap_32kb_length;

struct virtual_allocation_control *vir_allocation_control;
uint8_t *vir_bitmap_2mb;
uint64_t vir_bitmap_2mb_length;

struct bios_memory_entry *entries;
uint16_t entry_count;

///////////////////////////////////////////////////////////////////////////////////
// internal routines
static void mark_page_used_32k(uint64_t page)
{
	// mark the 4kb pages as used
	bitmap_4kb[page] = 0xff;
	page <<= 3;
	//printf("4k page %u 0x%x", page, bitmap_4kb[page / 8]);
	
	// mark the 8kb pages as used
	page >>= 1;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_8kb[byte] |= (0xf << bit);
	//printf("8k page %u 0x%x", page, bitmap_8kb[byte]);

	// mark the 16kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] |= (3 << bit);
	//printf("16k page %u 0x%x", page, bitmap_16kb[byte]);

	// mark the 32kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_32kb[byte] |= (1 << bit);
	//printf("32k page %u 0x%x", page, bitmap_32kb[byte]);
	
}

static void mark_page_used_16k(uint64_t page)
{
	// mark the 4kb pages as used
	page <<= 2;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] |= (0xf << bit);
	//printf("4k page %u 0x%x", page, bitmap_4kb[byte]);

	// mark the 8kb pages as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_8kb[byte] |= (3 << bit);
	//printf("8k page %u 0x%x", page, bitmap_8kb[byte]);

	// mark the 16kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] |= (1 << bit);
	//printf("16k page %u 0x%x", page, bitmap_16kb[byte]);

	// mark the 32kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_32kb[byte] |= (1 << bit);
	//printf("32k page %u 0x%x", page, bitmap_32kb[byte]);
}

static void mark_page_used_8k(uint64_t page)
{
	// mark the 4kb pages as used
	page <<= 1;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] |= (3 << bit);
	//printf("4k page %u 0x%x", page, bitmap_4kb[byte]);

	// mark the 8kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_8kb[byte] |= (1 << bit);
	//printf("8k page %u 0x%x", page, bitmap_8kb[byte]);

	// mark the 16kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] |= (1 << bit);
	//printf("16k page %u 0x%x", page, bitmap_16kb[byte]);

	// mark the 32kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_32kb[byte] |= (1 << bit);
	//printf("32k page %u 0x%x", page, bitmap_32kb[byte]);
}

static void mark_page_used_4k(uint64_t page)
{
	// mark the 4kb page as used
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] |= (1 << bit);
	//printf("4k page %u 0x%x", page, bitmap_4kb[byte]);

	// mark the 8kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_8kb[byte] |= (1 << bit);
	//printf("8k page %u 0x%x", page, bitmap_8kb[byte]);

	// mark the 16kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] |= (1 << bit);
	//printf("16k page %u 0x%x", page, bitmap_16kb[byte]);

	// mark the 32kb page as used
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_32kb[byte] |= (1 << bit);
	//printf("32k page %u 0x%x", page, bitmap_32kb[byte]);
}

static void mark_page_free_32k(uint64_t page)
{
	// need this for later
	uint64_t p = page;

	// mark the 4kb pages as free
	bitmap_4kb[page] = 0;
	page <<= 3;

	// mark 8kb page as free
	page >>= 1;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_8kb[byte] &= ~(0xf << bit);

	// mark 16kb page as free
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] &= ~(3 << bit);

	// mark 32kb page as free
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_32kb[byte] &= ~(1 << bit);

	//printf("4k page %u 0x%x", p << 3, bitmap_4kb[(p << 3) / 8]);
	//printf("8k page %u 0x%x", p << 2, bitmap_8kb[(p << 2) / 8]);
	//printf("16k page %u 0x%x", p << 1, bitmap_16kb[(p << 1) / 8]);
	//printf("32k page %u 0x%x", p, bitmap_32kb[p / 8]);
}

static void mark_page_free_16k(uint64_t page)
{
	// need this for later
	uint64_t p = page;

	// mark the 4kb pages as free
	page <<= 2;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] &= ~(0xf << bit);

	// mark 8kb page as free
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_8kb[byte] &= ~(3 << bit);

	// mark 16kb page as free
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_16kb[byte] &= ~(1 << bit);

	// mark 32kb page as free
	// if both 16kb pages are available
	bit &= 0x6;
	if(((bitmap_16kb[byte] >> bit) & 0x3) == 0)
	{
		// mark 32kb page as free
		page >>= 1;
		byte = page / 8;
		bit = page % 8;
		bitmap_32kb[byte] &= ~(1 << bit);
	}

	//printf("4k page %u 0x%x", p << 2, bitmap_4kb[(p << 2) / 8]);
	//printf("8k page %u 0x%x", p << 1, bitmap_8kb[(p << 1) / 8]);
	//printf("16k page %u 0x%x", p, bitmap_16kb[p / 8]);
	//printf("32k page %u 0x%x", p >> 1, bitmap_32kb[(p >> 1) / 8]);
}


static void mark_page_free_8k(uint64_t page)
{
	// need this for later
	uint64_t p = page;

	// mark the 4kb pages as free
	page <<= 1;
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] &= ~(3 << bit);

	// mark 8kb page as free
	page >>= 1;
	byte = page / 8;
	bit = page % 8;
	bitmap_8kb[byte] &= ~(1 << bit);

	// mark 16kb page as free
	// if both 8kb pages are available
	bit &= 6;
	if(((bitmap_8kb[byte] >> bit) & 0x3) == 0)
	{
		// mark 16kb page as free
		page >>= 1;
		byte = page / 8;
		bit = page % 8;
		bitmap_16kb[byte] &= ~(1 << bit);

		// mark 32kb page as free
		// if both 16kb pages are available
		bit &= 0x6;
		if(((bitmap_16kb[byte] >> bit) & 0x3) == 0)
		{
			// mark 32kb page as free
			page >>= 1;
			byte = page / 8;
			bit = page % 8;
			bitmap_32kb[byte] &= ~(1 << bit);
		}
	}

	//printf("4k page %u 0x%x", p << 1, bitmap_4kb[(p << 1) / 8]);
	//printf("8k page %u 0x%x", p, bitmap_8kb[p / 8]);
	//printf("16k page %u 0x%x", p >> 1, bitmap_16kb[(p >> 1) / 8]);
	//printf("32k page %u 0x%x", p >> 2, bitmap_32kb[(p >> 2) / 8]);
}

static void mark_page_free_4k(uint64_t page)
{
	// need this for later
	uint64_t p = page;

	// mark the 4kb page as free
	uint64_t byte = page / 8;
	uint64_t bit = page % 8;
	bitmap_4kb[byte] &= ~(1 << bit);

	// mark the 8kb page as free
	// if both 4kb pages are available
	bit &= 0x6;
	if(((bitmap_4kb[byte] >> bit) & 0x3) == 0)
	{
		// mark 8kb page as free
		page >>= 1;
		byte = page / 8;
		bit = page % 8;
		bitmap_8kb[byte] &= ~(1 << bit);

		// mark 16kb page as free
		// if both 8kb pages are available
		bit &= 6;
		if(((bitmap_8kb[byte] >> bit) & 0x3) == 0)
		{
			// mark 16kb page as free
			page >>= 1;
			byte = page / 8;
			bit = page % 8;
			bitmap_16kb[byte] &= ~(1 << bit);

			// mark 32kb page as free
			// if both 16kb pages are available
			bit &= 0x6;
			if(((bitmap_16kb[byte] >> bit) & 0x3) == 0)
			{
				// mark 32kb page as free
				page >>= 1;
				byte = page / 8;
				bit = page % 8;
				bitmap_32kb[byte] &= ~(1 << bit);
			}
		}
	}

	//printf("4k page %u 0x%x", p, bitmap_4kb[p / 8]);
	//printf("8k page %u 0x%x", p >> 1, bitmap_8kb[(p >> 1) / 8]);
	//printf("16k page %u 0x%x", p >> 2, bitmap_16kb[(p >> 2) / 8]);
	//printf("32k page %u 0x%x", p >> 3, bitmap_32kb[(p >> 3) / 8]);
}

static uint64_t get_page_32kb(PAGE_TYPE page_type)
{
	// setup variables
	uint64_t address = 0;
	uint64_t page = 0;
	uint64_t max_page = (phys_allocation_control->highest_address + 1) >> 15;

	// check if looking for post or pre 4GB
	if(page_type == PAGE_TYPE_HIGH)
	{
		// check if post 4GB memory is available
		if(max_page < 0x20000)
		{
			// post 4GB memory not available
			return 0;
		}

		// post_4GB memory is available
		// get starting page
		page = phys_allocation_control->post_4gb_allocate_32kb_next >> 15;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->post_4gb_allocate_32kb_next += 0x8000;
	}
	else if(page_type == PAGE_TYPE_LOW)
	{
		// allocating from below 4GB
		// set max page to 4GB limit if highest address is higher
		if(max_page > 0x20000)
			max_page = 0x20000;

		// set starting page from hint
		page = phys_allocation_control->sub_4gb_allocate_32kb_next >> 15;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->sub_4gb_allocate_32kb_next += 0x8000;
	}
	else
	{
		// bad page type given
		return 0;
	}

	// go through each page
	while(page < max_page)
	{
		// get bit and byte for bitmap
		uint64_t byte = page / 8;
		uint64_t bit = page % 8;

		// check if the page is available
		uint8_t value = (bitmap_32kb[byte] >> bit) & 1;
		if(value == 0)
		{
			// page is available
			// mark it as used
			mark_page_used_32k(page);

			// get address from page
			address = page << 15;

			// finished
			break;
		}

		// haven't found a free page
		page++;
	}

	return address;
}

static uint64_t get_page_16kb(PAGE_TYPE page_type)
{
	// setup variables
	uint64_t address = 0;
	uint64_t page = 0;
	uint64_t max_page = (phys_allocation_control->highest_address + 1) >> 14;

	// check if looking for post or pre 4GB
	if(page_type == PAGE_TYPE_HIGH)
	{
		// check if post 4GB memory is available
		if(max_page < 0x40000)
		{
			// post 4GB memory not available
			return 0;
		}

		// post_4GB memory is available
		// get starting page
		page = phys_allocation_control->post_4gb_allocate_16kb_next >> 14;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->post_4gb_allocate_16kb_next += 0x4000;
	}
	else if(page_type == PAGE_TYPE_LOW)
	{
		// allocating from below 4GB
		// set max page to 4GB limit if highest address is higher
		if(max_page > 0x40000)
			max_page = 0x40000;

		// set starting page from hint
		page = phys_allocation_control->sub_4gb_allocate_16kb_next >> 14;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->sub_4gb_allocate_16kb_next += 0x4000;
	}
	else
	{
		// bad page type given
		return 0;
	}

	// go through each page
	while(page < max_page)
	{
		// get bit and byte for bitmap
		uint64_t byte = page / 8;
		uint64_t bit = page % 8;

		// check if the page is available
		uint8_t value = (bitmap_16kb[byte] >> bit) & 1;
		if(value == 0)
		{
			// page is available
			// mark it as used
			mark_page_used_16k(page);

			// get address from page
			address = page << 14;

			// finished
			break;
		}

		// haven't found a free page
		page++;
	}

	return address;
}

static uint64_t get_page_8kb(PAGE_TYPE page_type)
{
	// setup variables
	uint64_t address = 0;
	uint64_t page = 0;
	uint64_t max_page = (phys_allocation_control->highest_address + 1) >> 13;

	// check if looking for post or pre 4GB
	if(page_type == PAGE_TYPE_HIGH)
	{
		// check if post 4GB memory is available
		if(max_page < 0x80000)
		{
			// post 4GB memory not available
			return 0;
		}

		// post_4GB memory is available
		// get starting page
		page = phys_allocation_control->post_4gb_allocate_8kb_next >> 13;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->post_4gb_allocate_8kb_next += 0x2000;
	}
	else if(page_type == PAGE_TYPE_LOW)
	{
		// allocating from below 4GB
		// set max page to 4GB limit if highest address is higher
		if(max_page > 0x80000)
			max_page = 0x80000;

		// set starting page from hint
		page = phys_allocation_control->sub_4gb_allocate_8kb_next >> 13;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->sub_4gb_allocate_8kb_next += 0x2000;
	}
	else
	{
		// bad page type given
		return 0;
	}

	// go through each page
	while(page < max_page)
	{
		// get bit and byte for bitmap
		uint64_t byte = page / 8;
		uint64_t bit = page % 8;

		// check if the page is available
		uint8_t value = (bitmap_8kb[byte] >> bit) & 1;
		if(value == 0)
		{
			// page is available
			// mark it as used
			mark_page_used_8k(page);

			// get address from page
			address = page << 13;

			// finished
			break;
		}

		// haven't found a free page
		page++;
	}

	return address;
}

static uint64_t get_page_4kb(PAGE_TYPE page_type)
{
	// setup variables
	uint64_t address = 0;
	uint64_t page = 0;
	uint64_t max_page = (phys_allocation_control->highest_address + 1) >> 12;

	// check if looking for post or pre 4GB
	if(page_type == PAGE_TYPE_HIGH)
	{
		// check if post 4GB memory is available
		if(max_page < 0x100000)
		{
			// post 4GB memory not available
			return 0;
		}

		// post_4GB memory is available
		// get starting page
		page = phys_allocation_control->post_4gb_allocate_4kb_next >> 12;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->post_4gb_allocate_4kb_next += 0x1000;
	}
	else if(page_type == PAGE_TYPE_LOW)
	{
		// allocating from below 4GB
		// set max page to 4GB limit if highest address is higher
		if(max_page > 0x100000)
			max_page = 0x100000;

		// set starting page from hint
		page = phys_allocation_control->sub_4gb_allocate_4kb_next >> 12;

		// check if hint is in range
		if(page > max_page)
		{
			// hint is not in range, there is no memory available to allocate
			return 0;
		}

		// increase hint
		phys_allocation_control->sub_4gb_allocate_4kb_next += 0x1000;
	}
	else
	{
		// bad page type given
		return 0;
	}

	// go through each page
	while(page < max_page)
	{
		// get bit and byte for bitmap
		uint64_t byte = page / 8;
		uint64_t bit = page % 8;

		// check if the page is available
		uint8_t value = (bitmap_4kb[byte] >> bit) & 1;
		if(value == 0)
		{
			// page is available
			// mark it as used
			mark_page_used_4k(page);

			// get address from page
			address = page << 12;

			// finished
			break;
		}

		// haven't found a free page
		page++;
	}

	return address;
}

static uint64_t free_page_32kb(uint64_t page_address)
{
	// calculate the page number from the address
	uint64_t page = page_address >> 15;

	// check if above 16mb
	if(page_address < 0x1000000)
	{
		return 0;
	}

	// check if page number is within the bitmap
	if((page / 8) >= bitmap_32kb_length)
	{
		return 0;
	}
	
	// check if the address is within a freeable page according to the tables
	uint64_t i;
	for(i = 0; i < entry_count; i++)
	{
		if((entries[i].type == 1) && (page_address <= entries[i].limit) && (page_address >= entries[i].base))
		{
			// check if the page is already allocated
			uint64_t byte = page / 8;
			uint64_t bit = page % 8;
			uint8_t value = bitmap_32kb[byte] & (1 << bit);
			if(value == 0)
			{
				// page is not already allocated
				return 0;
			}

			// this page is freeable
			// mark the page as free
			mark_page_free_32k(page);

			// set the appropriate hint
			if(page_address < 0x100000000)
			{
				// mark page low
				if(phys_allocation_control->sub_4gb_allocate_32kb_next > page_address)
				{
					phys_allocation_control->sub_4gb_allocate_32kb_next = page_address;
				}
			}
			else
			{
				// mark page high
				if(phys_allocation_control->post_4gb_allocate_32kb_next > page_address)
				{
					phys_allocation_control->post_4gb_allocate_32kb_next = page_address;
				}
			}
	
			return page << 15;
		}
	}

	// page is not freeable
	return 0;
}

static uint64_t free_page_16kb(uint64_t page_address)
{
	// calculate the page number from the address
	uint64_t page = page_address >> 14;

	// check if above 16mb
	if(page_address < 0x1000000)
	{
		return 0;
	}

	// check if page number is within the bitmap
	if((page / 8) >= bitmap_16kb_length)
	{
		return 0;
	}
	
	// check if the address is within a freeable page according to the tables
	uint64_t i;
	for(i = 0; i < entry_count; i++)
	{
		if((entries[i].type == 1) && (page_address <= entries[i].limit) && (page_address >= entries[i].base))
		{
			// check if the page is already allocated
			uint64_t byte = page / 8;
			uint64_t bit = page % 8;
			uint8_t value = bitmap_16kb[byte] & (1 << bit);
			if(value == 0)
			{
				// page is not already allocated
				return 0;
			}

			// this page is freeable
			// mark the page as free
			mark_page_free_16k(page);

			// set the appropriate hint
			if(page_address < 0x100000000)
			{
				// mark page low
				if(phys_allocation_control->sub_4gb_allocate_16kb_next > page_address)
				{
					phys_allocation_control->sub_4gb_allocate_16kb_next = page_address;
				}
			}
			else
			{
				// mark page high
				if(phys_allocation_control->post_4gb_allocate_16kb_next > page_address)
				{
					phys_allocation_control->post_4gb_allocate_16kb_next = page_address;
				}
			}
	
			return page << 14;
		}
	}

	// page is not freeable
	return 0;
}

static uint64_t free_page_8kb(uint64_t page_address)
{
	// calculate the page number from the address
	uint64_t page = page_address >> 13;

	// check if above 16mb
	if(page_address < 0x1000000)
	{
		return 0;
	}

	// check if page number is within the bitmap
	if((page / 8) >= bitmap_8kb_length)
	{
		return 0;
	}

	// check if the address is within a freeable page according to the tables
	uint64_t i;
	for(i = 0; i < entry_count; i++)
	{
		if((entries[i].type == 1) && (page_address <= entries[i].limit) && (page_address >= entries[i].base))
		{
			// check if the page is already allocated
			uint64_t byte = page / 8;
			uint64_t bit = page % 8;
			uint8_t value = bitmap_8kb[byte] & (1 << bit);
			if(value == 0)
			{
				// page is not already allocated
				return 0;
			}

			// this page is freeable
			// mark the page as free
			mark_page_free_8k(page);

			// set the appropriate hint
			if(page_address < 0x100000000)
			{
				// mark page low
				if(phys_allocation_control->sub_4gb_allocate_8kb_next > page_address)
				{
					phys_allocation_control->sub_4gb_allocate_8kb_next = page_address;
				}
			}
			else
			{
				// mark page high
				if(phys_allocation_control->post_4gb_allocate_8kb_next > page_address)
				{
					phys_allocation_control->post_4gb_allocate_8kb_next = page_address;
				}
			}
	
			return page << 13;
		}
	}

	// page is not freeable
	return 0;
}

static uint64_t free_page_4kb(uint64_t page_address)
{
	// calculate the page number from the address
	uint64_t page = page_address >> 12;

	// check if above 16mb
	if(page_address < 0x1000000)
	{
		return 0;
	}

	// check if page number is within the bitmap
	if((page / 8) >= bitmap_4kb_length)
	{
		return 0;
	}

	// check if the address is within a freeable page according to the tables
	uint64_t i;
	for(i = 0; i < entry_count; i++)
	{
		if((entries[i].type == 1) && (page_address <= entries[i].limit) && (page_address >= entries[i].base))
		{
			// check if the page is already allocated
			uint64_t byte = page / 8;
			uint64_t bit = page % 8;
			uint8_t value = bitmap_4kb[byte] & (1 << bit);
			if(value == 0)
			{
				// page is not already allocated
				return 0;
			}

			// this page is freeable
			// mark the page as free
			mark_page_free_4k(page);

			// set the appropriate hint
			if(page_address < 0x100000000)
			{
				// mark page low
				if(phys_allocation_control->sub_4gb_allocate_4kb_next > page_address)
				{
					phys_allocation_control->sub_4gb_allocate_4kb_next = page_address;
				}
			}
			else
			{
				// mark page high
				if(phys_allocation_control->post_4gb_allocate_4kb_next > page_address)
				{
					phys_allocation_control->post_4gb_allocate_4kb_next = page_address;
				}
			}
	
			return page << 12;
		}
	}

	// page is not freeable
	return 0;
}

///////////////////////////////////////////////////////////////////////////////////
// top level allocation/deallocation routines

static struct lock_table l;


// returns the address of the newly allocated page
// or zero if none are available
uint64_t memory_get_page(PAGE_SIZE page_size, PAGE_TYPE page_type)
{
	// lock memory system
	lock_no_IRQ(&l);

	// check memory size asked for
	uint64_t to_return = 0;
	if(page_size == PAGE_SIZE_4K)
	{
		to_return = get_page_4kb(page_type);
	}
	else if(page_size == PAGE_SIZE_8K)
	{
		to_return = get_page_8kb(page_type);
	}
	else if(page_size == PAGE_SIZE_16K)
	{
		to_return = get_page_16kb(page_type);
	}
	else if(page_size == PAGE_SIZE_32K)
	{
		to_return = get_page_32kb(page_type);
	}

	if(to_return > 0)
	{
		phys_allocation_control->total_memory_available -= page_size;
	}

	// unlock
	unlock_no_IRQ(&l);

	// finished
	return to_return;
}

// returns the address of the freed page
// or zero if the page was not freeable
uint64_t memory_free_page(uint64_t page_address, PAGE_SIZE page_size)
{
	// lock memory system
	lock_no_IRQ(&l);

	// check memory size
	uint64_t to_return = 0;
	if(page_size == PAGE_SIZE_4K)
	{
		to_return = free_page_4kb(page_address);
	}
	else if(page_size == PAGE_SIZE_8K)
	{
		to_return = free_page_8kb(page_address);
	}
	else if(page_size == PAGE_SIZE_16K)
	{
		to_return = free_page_16kb(page_address);
	}
	else if(page_size == PAGE_SIZE_32K)
	{
		to_return = free_page_32kb(page_address);
	}
	
	if(to_return > 0)
	{
		phys_allocation_control->total_memory_available += page_size;
	}

	// unlock
	unlock_no_IRQ(&l);

	// finished
	return to_return;
}